package com.xiaopo.flying.puzzle.straight;

import com.xiaopo.flying.puzzle.Line;
import com.xiaopo.flying.puzzle.ext.PointF;
import com.xiaopo.flying.puzzle.ext.RectF;
import ohos.agp.utils.Point;

import static java.lang.Math.max;
import static java.lang.Math.min;

/**
 * @author wupanjie
 */
class StraightLine implements Line {
    private PointF start;
    private PointF end;

    private PointF previousStart = new PointF();
    private PointF previousEnd = new PointF();

    public Direction direction = Direction.HORIZONTAL;

    StraightLine attachLineStart;
    StraightLine attachLineEnd;

    private Line upperLine;
    private Line lowerLine;

    private RectF bounds = new RectF();

    StraightLine(PointF start, PointF end) {
        this.start = start;
        this.end = end;

        if (start.getPointX() == end.getPointX()) {
            direction = Direction.VERTICAL;
        } else if (start.getPointY() == end.getPointY()) {
            direction = Direction.HORIZONTAL;
        }
    }

    @Override
    public float length() {
        return (float) Math.sqrt(Math.pow(end.getPointX() - start.getPointX(), 2) + Math.pow(end.getPointY() - start.getPointY(), 2));
    }

    @Override
    public PointF startPoint() {
        return start;
    }

    @Override
    public PointF endPoint() {
        return end;
    }

    @Override
    public Line lowerLine() {
        return lowerLine;
    }

    @Override
    public Line upperLine() {
        return upperLine;
    }

    @Override
    public Line attachStartLine() {
        return attachLineStart;
    }

    @Override
    public Line attachEndLine() {
        return attachLineEnd;
    }

    @Override
    public void setLowerLine(Line lowerLine) {
        this.lowerLine = lowerLine;
    }

    @Override
    public void setUpperLine(Line upperLine) {
        this.upperLine = upperLine;
    }

    void setAttachLineStart(StraightLine attachLineStart) {
        this.attachLineStart = attachLineStart;
    }

    void setAttachLineEnd(StraightLine attachLineEnd) {
        this.attachLineEnd = attachLineEnd;
    }

    @Override
    public Direction direction() {
        return direction;
    }

    @Override
    public float slope() {
        return direction == Direction.HORIZONTAL ? 0 : Float.MAX_VALUE;
    }

    @Override
    public boolean contains(float x, float y, float extra) {
        if (direction == Direction.HORIZONTAL) {
            bounds.left = start.getPointX();
            bounds.right = end.getPointX();
            bounds.top = start.getPointY() - extra / 2;
            bounds.bottom = start.getPointY() + extra / 2;
        } else if (direction == Direction.VERTICAL) {
            bounds.top = start.getPointY();
            bounds.bottom = end.getPointY();
            bounds.left = start.getPointX() - extra / 2;
            bounds.right = start.getPointX() + extra / 2;
        }

        return bounds.contains(x, y);
    }

    @Override
    public void prepareMove() {
        previousStart.set(start);
        previousEnd.set(end);
    }

    @Override
    public boolean move(float offset, float extra) {
        if (direction == Direction.HORIZONTAL) {
            if (previousStart.getPointY() + offset < lowerLine.maxY() + extra
                || previousStart.getPointY() + offset > upperLine.minY() - extra
                || previousEnd.getPointY() + offset < lowerLine.maxY() + extra
                || previousEnd.getPointY() + offset > upperLine.minY() - extra) {
                return false;
            }

            start.setPointY(previousStart.getPointY() + offset);
            end.setPointY(previousEnd.getPointY() + offset);
        } else {
            if (previousStart.getPointX() + offset < lowerLine.maxX() + extra
                || previousStart.getPointX() + offset > upperLine.minX() - extra
                || previousEnd.getPointX() + offset < lowerLine.maxX() + extra
                || previousEnd.getPointX() + offset > upperLine.minX() - extra) {
                return false;
            }

            start.setPointX(previousStart.getPointX() + offset);
            end.setPointX(previousEnd.getPointX() + offset);
        }

        return true;
    }

    @Override
    public void update(float layoutWidth, float layoutHeight) {
        if (direction == Direction.HORIZONTAL) {
            if (attachLineStart != null) {
                start.setPointX(attachLineStart.getPosition());
            }
            if (attachLineEnd != null) {
                end.setPointX(attachLineEnd.getPosition());
            }
        } else if (direction == Direction.VERTICAL) {
            if (attachLineStart != null) {
                start.setPointY(attachLineStart.getPosition());
            }
            if (attachLineEnd != null) {
                end.setPointY(attachLineEnd.getPosition());
            }
        }
    }

    public float getPosition() {
        if (direction == Direction.HORIZONTAL) {
            return start.getPointY();
        } else {
            return start.getPointX();
        }
    }

    @Override
    public float minX() {
        return min(start.getPointX(), end.getPointX());
    }

    @Override
    public float maxX() {
        return max(start.getPointX(), end.getPointX());
    }

    @Override
    public float minY() {
        return min(start.getPointY(), end.getPointY());
    }

    @Override
    public float maxY() {
        return max(start.getPointY(), end.getPointY());
    }

    @Override
    public void offset(float x, float y) {
        start.offset(x, y);
        end.offset(x, y);
    }

    @Override
    public String toString() {
        return "start --> " + start.toString() + ",end --> " + end.toString();
    }
}
